/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "modifier_ng/custom/rs_custom_modifier.h"

#include "command/rs_node_command.h"
#include "pipeline/rs_draw_cmd_list.h"
#include "pipeline/rs_node_map.h"
#include "pipeline/rs_recording_canvas.h"
#include "platform/common/rs_log.h"
#include "ui/rs_canvas_node.h"

namespace OHOS::Rosen::ModifierNG {
RSDrawingContext RSCustomModifierHelper::CreateDrawingContext(std::shared_ptr<RSNode> node)
{
    if (!node) {
        RS_LOGE("RSCustomModifierHelper::FinishDrawing: node is nullptr");
        return { nullptr };
    }
    if (!node->IsInstanceOf<RSCanvasNode>()) {
        RS_LOGW("RSCustomModifierHelper::FinishDrawing: node is not a RSCanvasNode, type=%{public}u", node->GetType());
        return { nullptr };
    }
    auto canvasNode = std::static_pointer_cast<RSCanvasNode>(node);
    auto recordingCanvas = new ExtendRecordingCanvas(canvasNode->GetPaintWidth(), canvasNode->GetPaintHeight());
    recordingCanvas->SetIsCustomTextType(canvasNode->GetIsCustomTextType());
    recordingCanvas->SetIsCustomTypeface(canvasNode->GetIsCustomTypeface());
    return { recordingCanvas, canvasNode->GetPaintWidth(), canvasNode->GetPaintHeight() };
}

std::shared_ptr<Drawing::DrawCmdList> RSCustomModifierHelper::FinishDrawing(RSDrawingContext& ctx)
{
    auto recordingCanvas = static_cast<ExtendRecordingCanvas*>(ctx.canvas);
    if (!recordingCanvas) {
        RS_LOGW("RSCustomModifierHelper::FinishDrawing: recordingCanvas is nullptr");
        return nullptr;
    }
    auto recording = recordingCanvas->GetDrawCmdList();
    if (!recording) {
        RS_LOGW("RSCustomModifierHelper::FinishDrawing: recording is nullptr");
        delete ctx.canvas;
        ctx.canvas = nullptr;
        return nullptr;
    }
    if (RSSystemProperties::GetDrawTextAsBitmap()) {
        // replace drawOpItem with cached one (generated by CPU)
        recording->GenerateCache();
    }
    delete ctx.canvas;
    ctx.canvas = nullptr;
    return recording;
}

void RSCustomModifier::UpdateDrawCmdList()
{
    auto node = node_.lock();
    if (node == nullptr) {
        return;
    }
    RSDrawingContext ctx = RSCustomModifierHelper::CreateDrawingContext(node);
    Draw(ctx);
    auto drawCmdList = RSCustomModifierHelper::FinishDrawing(ctx);
    auto propertyType = GetInnerPropertyType();
    auto it = properties_.find(propertyType);
    if (it != properties_.end()) {
        auto property = std::static_pointer_cast<RSAnimatableProperty<Drawing::DrawCmdListPtr>>(it->second);
        if (property->isCustom_) {
            property->showingValue_ = drawCmdList;
        } else {
            property->stagingValue_ = drawCmdList;
        }
        MarkNodeDirty();
        if (property->isCustom_) {
            property->MarkCustomModifierDirty();
        }
    } else {
        auto property = std::make_shared<RSAnimatableProperty<Drawing::DrawCmdListPtr>>(drawCmdList);
        property->SetPropertyTypeNG(propertyType);
        properties_[propertyType] = property;
        SetPropertyThresholdType(propertyType, property);
        property->Attach(*node, weak_from_this());
        MarkNodeDirty();
    }
}

void RSCustomModifier::UpdateToRender()
{
    auto node = node_.lock();
    if (node == nullptr) {
        return;
    }
    RSDrawingContext ctx = RSCustomModifierHelper::CreateDrawingContext(node);
    Draw(ctx);
    auto drawCmdList = RSCustomModifierHelper::FinishDrawing(ctx);
    bool isEmpty = drawCmdList == nullptr;
    if (lastDrawCmdListEmpty_ && isEmpty) {
        return;
    }
    if (drawCmdList) {
        drawCmdList->SetNoNeedUICaptured(noNeedUICaptured_);
        drawCmdList->SetIsNeedUnmarshalOnDestruct(!node->IsRenderServiceNode());
    }
    lastDrawCmdListEmpty_ = isEmpty;
    auto it = properties_.find(GetInnerPropertyType());
    if (it == properties_.end()) {
        return;
    }
    if (it->second == nullptr) {
        return;
    }

    auto property = std::static_pointer_cast<RSAnimatableProperty<Drawing::DrawCmdListPtr>>(it->second);
    if (property->isCustom_) {
        property->showingValue_ = drawCmdList;
    } else {
        property->stagingValue_ = drawCmdList;
    }
    UpdateProperty(node, drawCmdList, property->GetId());
}

void RSCustomModifier::UpdateProperty(
    std::shared_ptr<RSNode> node, std::shared_ptr<Drawing::DrawCmdList> drawCmdList, PropertyId propertyId)
{
    if (contentTransitionType_ == ContentTransitionType::OPACITY &&
        timingCurve_.type_ == RSAnimationTimingCurve::CurveType::INTERPOLATING && timingProtocol_.GetDuration() > 0) {
        auto propertyCallback = [this, &drawCmdList]() {
            Setter<RSAnimatableProperty, std::shared_ptr<Drawing::DrawCmdList>>(GetInnerPropertyType(), drawCmdList);
        };
        RSNode::Animate(node->GetRSUIContext(), timingProtocol_, timingCurve_, propertyCallback);
        return;
    }

    std::unique_ptr<RSCommand> command =
        std::make_unique<RSUpdatePropertyDrawCmdListNG>(node->GetId(), drawCmdList, propertyId);
    node->AddCommand(command, node->IsRenderServiceNode());
    if (node->NeedForcedSendToRemote()) {
        std::unique_ptr<RSCommand> commandForRemote =
            std::make_unique<RSUpdatePropertyDrawCmdListNG>(node->GetId(), drawCmdList, propertyId);
        node->AddCommand(commandForRemote, true, node->GetFollowType(), node->GetId());
    }
}
} // namespace OHOS::Rosen::ModifierNG
